package com.luxsoft.siipap.em.replica.importadores.cxc;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.MessageFormat;
import java.util.List;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;

import com.luxsoft.siipap.cxc.model.Aplicacion;
import com.luxsoft.siipap.cxc.model.AplicacionDePago;
import com.luxsoft.siipap.cxc.model.Cargo;
import com.luxsoft.siipap.cxc.model.OrigenDeOperacion;
import com.luxsoft.siipap.cxc.model.Pago;
import com.luxsoft.siipap.cxc.model.PagoConCheque;
import com.luxsoft.siipap.cxc.model.PagoConDeposito;
import com.luxsoft.siipap.cxc.model.PagoConEfectivo;
import com.luxsoft.siipap.cxc.model.PagoConTarjeta;
import com.luxsoft.siipap.cxc.model.PagoDeDiferencias;
import com.luxsoft.siipap.em.replica.importadores.ImportadorSupport;
import com.luxsoft.siipap.em.replica.importadores.ventas.ImportadorDeVentas;
import com.luxsoft.siipap.model.Periodo;
import com.luxsoft.siipap.model.core.Cliente;
import com.luxsoft.siipap.service.ServiceLocator2;
import com.luxsoft.siipap.util.DBUtils;
import com.luxsoft.siipap.util.MonedasUtils;
import com.luxsoft.siipap.util.ValidationUtils;

/**
 * Importa los pagos y sus aplicaciones
 * 
 * @author Ruben Cancino Ramos
 *
 */
public class ImportadorDePagos {
	
	private int total;
	
	private int current;
	
	private Logger logger=Logger.getLogger(getClass());
	
	
	
	protected void setProgress(int progress){
		System.out.print(".");
	}
	
	protected void updateCurrent(){
		current++;		
		if(total!=0){
			int progress=current*100/this.total;			
			setProgress(progress);
		}
	}
	
	/**
	 * Importa un pago desde SiipapWin (Oracle)
	 * 
	 * @param id
	 * @return
	 */
	public Pago importar(Long id){
		String sql="SELECT * FROM SW_PAGOM WHERE PAGOM_ID=? ";
		List<Pago> res=getOrigenTemplate().query(sql, new Object[]{id}, new PagoMapper());		
		if((res==null)|| res.isEmpty()){
			logger.info("No econtro el pago en siipapwin PAGOM_ID="+id);
			return null;
		}
		Pago cargo= res.get(0);
		try {
			insertarPartidas(cargo);
			return persistir(cargo,true);
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * Importa todas las notas de un periodo para el tipo indicado
	 * 
	 * @param periodo
	 * 
	 */
	public void importar(final Periodo periodo){
		
		String sql="SELECT * FROM SW_PAGOM  A WHERE A.FECHA BETWEEN ? AND ?  AND TIPO in (\'N\',\'K\')  AND IMPORTE>0";
		Object[] params=new Object[]{
			new SqlParameterValue(Types.DATE,periodo.getFechaInicial())	
			,new SqlParameterValue(Types.DATE,periodo.getFechaFinal())
		};
		int tot=getOrigenTemplate().queryForInt("SELECT count(*) FROM SW_PAGOM   WHERE FECHA BETWEEN ? AND ? AND TIPO in (\'N\',\'K\') AND IMPORTE>0 ",params);
		logger.info("Total de registros a importar: "+tot );
		setTotal(tot);
		logger.info(MessageFormat.format("Mapeando para el periodo {0}",periodo));
		List<Pago> pagos=getOrigenTemplate().query(sql, params,new PagoMapper());
		for (Pago pago : pagos) {
			if(pago==null) continue;
			try {
				Assert.notNull(pago.getCliente(),"No existe el cliente para el pago id:"+pago.getSiipapId());
				insertarPartidas(pago);				
				pago=persistir(pago,true);
				//pago.acutalizarDisponible();
				//getHibernate().merge(pago);
				updateCurrent();
			} catch (Exception e) {
				e.printStackTrace();
				logger.error(e);
			}
			 	
		}
	}
	
	
	protected Pago persistir(final Pago pago,boolean confirmar){
		if(confirmar){
			Pago target=buscarPagoPorSiipapId(pago.getSiipapId());
			if(target==null){			
				target=pago;
			}
			else{
				//copiar propiedades necesarias
				logger.info("Persistiendo pago existente: "+target.getSiipapId()+" Id:"+target.getId());
				target.getAplicaciones().clear();
				BeanUtils.copyProperties(pago, target,new String[]{"id","version","aplicaciones"});
				
				target.setImporte(pago.getImporte());
				target.setImpuesto(pago.getImpuesto());
				target.setTotal(pago.getTotal());
				
				for(Aplicacion det:pago.getAplicaciones()){
					target.agregarAplicacion(det);
					det.actualizarDetalle();
				}
				target.acutalizarDisponible();
				
			}
			if(!ValidationUtils.isValid(target)){
				ValidationUtils.debugValidation(pago);
			}			
			Pago res= (Pago)getHibernate().merge(target);			
			return res;
		}else{
			getHibernate().merge(pago);
			return pago;
		}
				
		
	}
	
	
	
	private void insertarPartidas(final Pago cargo){
		String sql="SELECT * FROM SW_PAGOS WHERE PAGOM_ID =? UNION SELECT * FROM SW_PAGOS WHERE PAGOM_ID IN(SELECT PAGOM_ID FROM SW_PAGOM WHERE ORIGEN_ID=?)";
		
		Object[] args=new Object[]{cargo.getSiipapId(),cargo.getSiipapId()};
		List<Aplicacion> partidas=getOrigenTemplate().query(sql, args, new AplicacionDePagoMapper());
		for (Aplicacion det : partidas) {
			cargo.agregarAplicacion(det);
			det.actualizarDetalle();
		}
	}
	
	
	public JdbcTemplate getOrigenTemplate(){
		return ServiceLocator2.getAnalisisJdbcTemplate();
	}
	
	public HibernateTemplate getHibernate(){
		return ServiceLocator2.getHibernateTemplate();
	}
	
	public Pago buscarPagoPorSiipapId(Long siipapId){
		List<Pago> data=getHibernate().find("from Pago p  left join fetch p.aplicaciones ap where p.siipapId=?", siipapId);		
		return data.isEmpty()?null:data.get(0);
	}
	
	private Cargo buscarCargo(Long id){
		List<Cargo> data=getHibernate().find("from Cargo c  where c.siipapWinId=?", id);		
		return data.isEmpty()?null:data.get(0);
	}

	public int getTotal() {
		return total;
	}

	public void setTotal(int total) {
		this.total = total;
	}
	
	private class PagoMapper implements RowMapper{
		
		public Pago getPago(){
			return new PagoConDeposito();
		}
		
		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			Long pagomid=rs.getLong("PAGOM_ID");
			
			try {
				String fp=rs.getString("FORMADP");
				Pago p=null;
				if("E".equals(fp)){
					p=new PagoConEfectivo();
				}else if("N".equals(fp) || "O".equals(fp) ){				
					PagoConDeposito pd=new PagoConDeposito();
					p=pd;
				}else if( ("B".equals(fp)) || ("C".equals(fp)) || ("Q".equals(fp)) ){
					p=new PagoConTarjeta();				
					
				}else if("H".equals(fp) || "Y".equals(fp) ){				
					p=new PagoConCheque();
				}else if("F".equals(fp) || "D".equals(fp) ){				
					PagoDeDiferencias pdd=new PagoDeDiferencias();
					if("F".equals(fp)){
						pdd.setCambiaria(Boolean.TRUE);
					}
					p=pdd;
				}else if("X".equals(fp)){
					p=new PagoConEfectivo();
					p.setComentario("Importado, orignalmente forma de pago X");
				}else{				
					throw new RuntimeException("Forma de pago no valida en el esquema actual: "+fp);
				}
				
				p.setBanco(rs.getString("BANCO"));
				String clave=rs.getString("CLAVE");
				Cliente c=ImportadorSupport.getInstance().buscarCliente(clave);
				if(c==null){				
					c=ImportadorDeClientes.getInstance().importar(clave);
				}
				p.setSiipapId(pagomid);
				p.setCliente(c);
				p.setComentario(rs.getString("COMENTARIO"));
				p.setFecha(rs.getDate("FECHA"));
				BigDecimal total=rs.getBigDecimal("IMPORTE");
				Assert.isTrue(total.doubleValue()>0,"Pago con importe cero :"+p.getSiipapId());
				p.setImporte(MonedasUtils.calcularImporteDelTotal(total));
				p.setImpuesto(MonedasUtils.calcularImpuesto(p.getImporte()));
				p.setTotal(total);
				p.setOrigen(OrigenDeOperacion.CRE);
				p.setSucursal(ImportadorSupport.getInstance().buscarSucursal(1));
				
				String referencia=rs.getString("REFERENCIA");
				if(p instanceof PagoConCheque){
					PagoConCheque pc=(PagoConCheque)p;
					pc.setNumero(NumberUtils.toLong(referencia));
					if("Y".equals(fp)){
						pc.setPostFechado(true);
					}
				}else if(p instanceof PagoConTarjeta){
					//PagoConTarjeta pt=(PagoConTarjeta)p;				
					
				}else if(p instanceof PagoConDeposito){
					PagoConDeposito pd=(PagoConDeposito)p;
					pd.setReferenciaBancaria(referencia);
					if("N".equals(fp))
						pd.setTransferencia(pd.getTotal());
					else
						pd.setEfectivo(pd.getTotal());
				}else if(p instanceof PagoConEfectivo){
					
				}
				String tipo=rs.getString("TIPO");
				if("K".equals(tipo))
					p.setAnticipo(true);
				return p;
			} catch (Exception e) {
				System.out.println(e.getMessage());
				System.out.println("No puedo mapear pagom_id="+pagomid);
				return null;
			}
			
		}		
	}
	
	private class AplicacionDePagoMapper implements RowMapper{
		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			AplicacionDePago a=new AplicacionDePago();
			Long ventaId=rs.getLong("VENTA_ID");
			Cargo cargo; 
			if(ventaId!=null && (ventaId.doubleValue()>0)){
				cargo=buscarCargo(ventaId);
				if(cargo==null){
					cargo=ImportadorDeVentas.instance().importar(ventaId);
					Assert.notNull(cargo,"No pudo importar la venta: "+ventaId);
				}
			}else{
				Long notaId=rs.getLong("NOTA_ID");
				cargo=buscarCargo(notaId);
				if(cargo==null){
					cargo=ImportadorDeNotasDeCargo.instance().importar(notaId);
					Assert.notNull(cargo,"No pudo importar  la nota de cargo: "+notaId);
				}
			}			
			a.setCargo(cargo);
			a.setComentario(rs.getString("COMENTARIO"));
			a.setFecha(rs.getDate("FECHA"));
			a.setImporte(rs.getBigDecimal("IMPORTE"));
			Long pagoid=rs.getLong("PAGO_ID");
			a.setSiipapId(pagoid);       
			return a;
		}		
	}
	
	private static ImportadorDePagos INSTANCE;
	
	public static ImportadorDePagos getInstance(){
		if(INSTANCE==null)
			INSTANCE=new ImportadorDePagos();
		return INSTANCE;
			
	}

	public static void main(String[] args) {
		DBUtils.whereWeAre();
		ImportadorDePagos imp=new ImportadorDePagos();
		//imp.importar(new Periodo("06/01/2009","06/01/2009"));
		Long[] ids={
				385180L
				};
		int row=0;
		for(Long l:ids){
			
			try {
				imp.importar(l);
				row++;
				System.out.println("Importado: "+l);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
}
